#pragma once

#include <stdio.h>
#include <utility>
#include <string>
#include <string.h>
#include "basetypes.h"
#include "crc.h"
#include "error_code.h"

typedef uint32_t filetime_t;
typedef int32_t  HResult;
typedef uint32_t DIR_SN;
typedef uint32_t USER_ID;
//typedef std::string string;

struct DIR_ID_STRUCT
{
	DIR_SN  _dirSN;
	USER_ID _userID;
};

union DIR_ID
{
	uint64_t	  _id;
	DIR_ID_STRUCT _parts;

	DIR_ID(): _id(0) { }
	DIR_ID(uint64_t id): _id(id) { }
	DIR_ID(USER_ID userID, DIR_SN dirSN) { _parts._dirSN = dirSN; _parts._userID = userID; }

	bool operator==(const DIR_ID& other) const {
		return (_id == other._id);
	}

	bool operator!=(const DIR_ID& other) const {
		return (_id != other._id);
	}
};


// Note: MAX_PATH = 260, i.e. the length of path + filename in UTF-16 <= 260 (include null terminator)
// I think the length of filename in UTF-8 <= 128 (include null terminator) is long enough
const uint32_t MAX_FILENAME = 128;

typedef uint32_t CompressType;
typedef uint32_t SecurityType;
typedef uint32_t FileType;

const size_t hash_multiplier = sizeof(size_t)==4? 2654435769U : 11400714819323198485ULL;

struct KEY_INFO
{
	DIR_ID          _parentID;
	char            _name[MAX_FILENAME];

	KEY_INFO() { }

	KEY_INFO(DIR_ID parentID, const char* name): _parentID(parentID)
	{ strncpy(_name, name, sizeof(_name) - 1); }

	KEY_INFO(DIR_ID parentID, const std::string& name): _parentID(parentID)
	{ strncpy(_name, name.c_str(), sizeof(_name) - 1); }

	virtual std::string GetKey() const 
	{
        char key[MAX_FILENAME];
        sprintf(key, "%u_%u_%s", _parentID._parts._userID, _parentID._parts._dirSN, _name);
		//std::string str((const char*)&_parentID._id, sizeof(_parentID._id)); 
        std::string str(key);
		return str;
	}

	size_t GetHash() const
	{
#ifdef __LP64__
		size_t h = _parentID._id;
#else
#ifdef _WIN64
		size_t h = _parentID._id;
#else
		size_t h = _parentID._parts._dirSN ^ _parentID._parts._userID;
#endif
#endif
		for( const char* c = _name; c < _name + MAX_FILENAME; ++c)
		{
			if (0 == *c)
				break;
			h = static_cast<size_t>(*c) ^ (h * hash_multiplier);
		}
		return h;
	}

	bool operator==(const KEY_INFO& other) const {
		return (_parentID == other._parentID) && strncmp(_name, other._name, sizeof(_name)) == 0;
	}

	bool operator!=(const KEY_INFO& other) const {
		return !(*this == other);
	}
};

inline size_t tbb_hasher(const KEY_INFO& key) {
    return key.GetHash();
}

struct DIR_INFO: KEY_INFO
{
	DIR_ID          _dirID;
    uint32_t        _fileCount;	// ? it's hard to sync
    uint32_t        _totalSize;	// ? it's hard to sync, it should be uint64_t ?
    filetime_t      _createTime;
    filetime_t      _updateTime; // ? it's hard to sync
	filetime_t      _deleteTime;
    bool            _isDeleted;

	DIR_INFO() { }

	DIR_INFO(DIR_ID parentID, const char* name): KEY_INFO(parentID, name) { }

	DIR_INFO(DIR_ID parentID, const std::string& name): KEY_INFO(parentID, name) { }
};

class DataLocation
{
public:
    char            _fileName[MAX_FILENAME];
    uint64_t        _offset;
    uint64_t        _length;
    
    DataLocation &operator = (const DataLocation &loc)
    {
        strcpy(_fileName, loc._fileName);
        _offset = loc._offset;
        _length = loc._length;
        
        return *this;
    }
};

struct FILE_INFO: KEY_INFO
{
    uint32_t        _fileVersion;
    filetime_t      _createTime;
    filetime_t      _deleteTime;
    uint64_t        _length;
    CompressType    _compressType;
    SecurityType    _securityType;
    bool            _isDeleted;
    bool            _isSharing;
    CRC32           _headCRC;
    CRC32           _dataCRC;
    DataLocation    _loc;

	FILE_INFO() { }

	FILE_INFO(DIR_ID parentID, const char* name): KEY_INFO(parentID, name) { }

	FILE_INFO(DIR_ID parentID, const std::string& name): KEY_INFO(parentID, name) { }
    
    std::string GetKeyWithVersion() const
    {
        char key[MAX_FILENAME];
        sprintf(key, "%u_%u_%s_%04u", _parentID._parts._userID, _parentID._parts._dirSN, _name, _fileVersion);
		//std::string str((const char*)&_parentID._id, sizeof(_parentID._id)); 
        std::string str(key);
		return str;
    }
};

struct FILE_DATA
{
    uint32_t        _version;
    USER_ID         _userID;
    FileType        _fileType;
    uint64_t        _length;
    CRC32           _dataCRC;
    int8_t*         _data;
    
    static uint32_t GetHeaderSize()
    {
        return 24;
        //return sizeof(FILE_DATA) - sizeof(const int8_t*);
    }
};

const DIR_SN ROOT_DIR_SN = 0;
const uint64_t CURRENT_FILE_DATA_VERSION = 0;

const HResult E_RETRY_LATER = ERROR_CODE_RETRY_LATER;
const HResult E_ITEM_NOT_FOUND = -20;

#ifndef S_OK
const HResult S_OK = ERROR_CODE_OK;
#endif
